/*
 moxl - Modular XML Library
 Copyright (c) 2007, Malcolm Sparks

 This program is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License
 as published by the Free Software Foundation; either version 2
 of the License, or (at your option) any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */
package moxl.tokenizer;

import java.io.IOException;
import java.util.LinkedList;

import moxl.reader.LocateableReader;

public class TokenizerStateMachine
{
	private AbstractTokenizerState state;
	private Token previousToken;
	private AbstractTokenizerState startState;

	/**
	 * This is used when scanning the tokens for the end of the DOCTYPE, pull-parser style. We can only be sure we have reached the end if we have accounted for
	 * other delimiters, including chevrons.
	 */
	@SuppressWarnings("PMD.LooseCoupling")
	private LinkedList<Character> delimiterStack = new LinkedList<Character>();

	public TokenizerStateMachine(AbstractTokenizerState startState)
	{
		this.startState = startState;
	}

	/**
	 * @return the next token, or null if no more tokens.
	 * @throws IOException
	 */
	public Token readToken(LocateableReader reader) throws IOException
	{
		Token token = new Token(reader.getLocation(), previousToken);
		state = startState;
		do
		{
			state.accept(this, token, reader);
		}
		while (!state.isFinishState());
		if (state.equals(EndOfFileState.getInstance()))
		{
			return null;
		}
		else
		{
			previousToken = token;
			return token;
		}
	}

	public void changeState(State newState)
	{
		this.state = (AbstractTokenizerState) newState;
	}

	public State getState()
	{
		return state;
	}

	public void setStartState(AbstractTokenizerState startState)
	{
		if (!startState.isStartState())
		{
			throw new IllegalArgumentException(startState + " is not a start state.");
		}
		this.startState = startState;
	}

	public Token getPreviousToken()
	{
		return previousToken;
	}

	public AbstractTokenizerState getStartState()
	{
		return startState;
	}

	public void pushDelimiter(char c)
	{
		delimiterStack.addFirst(c);
	}

	public boolean isDelimiterStackEmpty()
	{
		return delimiterStack.isEmpty();
	}

	public char pop()
	{
		return delimiterStack.removeFirst();
	}
}
